package com.bf.electroplating.service.compoent;

import com.bf.electroplating.config.HJ212Config;
import com.bf.electroplating.pojo.entity.MessageLog;
import com.bf.electroplating.service.MessageLogService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 * HJ212协议服务器
 * 在项目启动时自动启动，接收基于HJ212协议的请求并返回确认信息
 */
@Slf4j
@Component
public class HJ212Server implements CommandLineRunner {
    
    @Autowired
    private HJ212Config hj212Config;
    
    @Autowired
    private HJ212PacketParser packetParser;

    @Autowired
    private MessageLogService messageLogService;
    
    private ServerSocketChannel serverSocketChannel;
    private Selector selector;
    private ExecutorService executorService;
    private ScheduledExecutorService scheduledExecutorService;
    private volatile boolean running = false;
    
    // 存储连接的客户端，key为客户端ID，value为SocketChannel
    private final ConcurrentHashMap<String, SocketChannel> connectedClients = new ConcurrentHashMap<>();
    // 存储客户端信息，key为客户端ID，value为客户端地址信息
    private final ConcurrentHashMap<String, String> clientInfo = new ConcurrentHashMap<>();
    // 存储MN值到客户端ID的映射，key为MN值，value为客户端ID
    private final ConcurrentHashMap<String, String> mnToClientId = new ConcurrentHashMap<>();
    // 存储客户端ID到MN值的映射，key为客户端ID，value为MN值
    private final ConcurrentHashMap<String, String> clientIdToMn = new ConcurrentHashMap<>();
    // 存储定时任务，key为MN值，value为定时任务Future
    private final ConcurrentHashMap<String, ScheduledFuture<?>> scheduledTasks = new ConcurrentHashMap<>();
    
    @Override
    public void run(String... args) throws Exception {
        if (!hj212Config.isEnabled()) {
            log.info("HJ212服务未启用，跳过启动");
            return;
        }
        
        log.info("启动HJ212服务器，端口: {}", hj212Config.getPort());
        startServer();
    }
    
    /**
     * 启动HJ212服务器
     */
    public void startServer() {
        try {
            // 创建线程池
            executorService = Executors.newFixedThreadPool(hj212Config.getMaxConnections());
            // 创建定时任务线程池
            scheduledExecutorService = Executors.newScheduledThreadPool(10);
            
            // 创建选择器
            selector = Selector.open();
            
            // 创建服务器套接字通道
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.socket().bind(new InetSocketAddress(hj212Config.getHost(), hj212Config.getPort()));
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            
            running = true;
            log.info("HJ212服务器启动成功，监听地址: {}:{}", hj212Config.getHost(), hj212Config.getPort());
            
            // 启动服务器监听线程
            new Thread(this::runServer, "HJ212-Server-Thread").start();
            
        } catch (IOException e) {
            log.error("启动HJ212服务器失败", e);
        }
    }
    
    /**
     * 运行服务器监听循环
     */
    private void runServer() {
        while (running) {
            try {
                // 等待事件
                int readyChannels = selector.select(1000);
                if (readyChannels == 0) {
                    continue;
                }
                
                // 处理就绪的通道
                Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
                while (keyIterator.hasNext()) {
                    SelectionKey key = keyIterator.next();
                    keyIterator.remove();
                    
                    try {
                        if (key.isAcceptable()) {
                            handleAccept(key);
                        } else if (key.isReadable()) {
                            handleRead(key);
                        }
                    } catch (Exception e) {
                        log.error("处理通道事件失败", e);
                        key.cancel();
                        try {
                            key.channel().close();
                        } catch (IOException ex) {
                            log.error("关闭通道失败", ex);
                        }
                    }
                }
                
            } catch (IOException e) {
                log.error("HJ212服务器运行异常", e);
            }
        }
    }
    
    /**
     * 处理连接接受事件
     */
    private void handleAccept(SelectionKey key) throws IOException {
        ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
        SocketChannel clientChannel = serverChannel.accept();
        clientChannel.configureBlocking(false);
        clientChannel.register(selector, SelectionKey.OP_READ);
        
        // 生成临时客户端ID并存储连接信息
        String tempClientId = "hj212_client_" + System.currentTimeMillis() + "_" + clientChannel.socket().getPort();
        String clientAddress = clientChannel.getRemoteAddress().toString();
        
        connectedClients.put(tempClientId, clientChannel);
        clientInfo.put(tempClientId, clientAddress);
        
        log.info("接受新的HJ212客户端连接: {} ({})", clientAddress, tempClientId);
        log.info("当前连接数: {}", connectedClients.size());
    }
    
    /**
     * 处理数据读取事件
     */
    private void handleRead(SelectionKey key) throws IOException {
        SocketChannel clientChannel = (SocketChannel) key.channel();
        ByteBuffer buffer = ByteBuffer.allocate(hj212Config.getBufferSize());
        
        int bytesRead = clientChannel.read(buffer);
        if (bytesRead == -1) {
            // 客户端断开连接
            String clientAddress = clientChannel.getRemoteAddress().toString();
            removeClientConnection(clientChannel);
            log.info("HJ212客户端断开连接: {}", clientAddress);
            key.cancel();
            clientChannel.close();
            return;
        }
        
        if (bytesRead > 0) {
            buffer.flip();
            byte[] data = new byte[bytesRead];
            buffer.get(data);
            
            // 异步处理数据包
            executorService.submit(() -> processPacket(clientChannel, data));
        }
    }
    
    /**
     * 移除客户端连接
     */
    private void removeClientConnection(SocketChannel clientChannel) {
        String clientAddress = null;
        String clientId = null;
        
        try {
            clientAddress = clientChannel.getRemoteAddress().toString();
        } catch (IOException e) {
            log.error("获取客户端地址失败", e);
        }
        
        // 查找并移除客户端连接
        for (var entry : connectedClients.entrySet()) {
            if (entry.getValue() == clientChannel) {
                clientId = entry.getKey();
                break;
            }
        }
        
        if (clientId != null) {
            connectedClients.remove(clientId);
            clientInfo.remove(clientId);
            
            // 清理MN相关映射
            String mn = clientIdToMn.remove(clientId);
            if (mn != null) {
                mnToClientId.remove(mn);
                log.info("移除客户端连接: {} ({}) (MN: {})", clientAddress, clientId, mn);
            } else {
                log.info("移除客户端连接: {} ({})", clientAddress, clientId);
            }
            log.info("当前连接数: {}", connectedClients.size());
        }
    }
    
    /**
     * 处理HJ212数据包
     */
    private void processPacket(SocketChannel clientChannel, byte[] data) {
        try {
            // 解析数据包
            HJ212PacketParser.HJ212Packet packet = packetParser.parsePacket(data);
            if (packet == null) {
                log.warn("无法解析HJ212数据包");
                sendErrorResponse(clientChannel, "0001", "数据包格式错误");
                return;
            }
            
            log.info("成功解析HJ212数据包: {}", packet.getMessage());
            
            // 解析MN值并更新客户端ID
            String mn = extractMnFromPacket(packet);
            if (mn != null) {
                updateClientIdWithMn(clientChannel, mn);
            }

            // 入库
            MessageLog messageLog = new MessageLog ();
            messageLog.setMn ( mn );
            messageLog.setMessage ( packet.getMessage () );
            messageLog.setAddress ( clientChannel.getRemoteAddress ().toString () );
            messageLogService.dateCheck ( messageLog );

            // 构建确认响应
            byte[] ackResponse = packetParser.buildAckResponse(packet);
            if (ackResponse != null) {
                sendResponse(clientChannel, ackResponse);
                log.info("已发送确认响应给客户端: {}", clientChannel.getRemoteAddress());
            } else {
                sendErrorResponse(clientChannel, "0002", "构建响应失败");
            }
            
        } catch (Exception e) {
            log.error("处理HJ212数据包失败", e);
            sendErrorResponse(clientChannel, "0003", "服务器内部错误");
        }
    }
    
    /**
     * 发送响应数据
     */
    private void sendResponse(SocketChannel clientChannel, byte[] response) {
        try {
            ByteBuffer buffer = ByteBuffer.wrap(response);
            while (buffer.hasRemaining()) {
                clientChannel.write(buffer);
            }
        } catch (IOException e) {
            log.error("发送响应失败", e);
        }
    }
    
    /**
     * 从数据包中提取MN值
     */
    private String extractMnFromPacket(HJ212PacketParser.HJ212Packet packet) {
        try {
            String message = packet.getMessage();
            if (message == null) {
                return null;
            }
            
            // 解析MN字段
            String[] fields = message.split(";");
            for (String field : fields) {
                if (field.startsWith("MN=")) {
                    String mn = field.substring(3);
                    log.info("提取到MN值: {}", mn);
                    return mn;
                }
            }
            
            log.warn("未找到MN字段");
            return null;
        } catch (Exception e) {
            log.error("提取MN值失败", e);
            return null;
        }
    }
    
    /**
     * 使用MN值更新客户端ID
     */
    private void updateClientIdWithMn(SocketChannel clientChannel, String mn) {
        try {
            // 查找当前客户端ID
            String currentClientId = null;
            for (var entry : connectedClients.entrySet()) {
                if (entry.getValue() == clientChannel) {
                    currentClientId = entry.getKey();
                    break;
                }
            }
            
            if (currentClientId == null) {
                log.warn("未找到客户端连接");
                return;
            }
            
            // 生成新的客户端ID（包含MN值）
            String newClientId = "hj212_mn_" + mn + "_" + System.currentTimeMillis();
            
            // 检查MN值是否已被其他客户端使用
            String existingClientId = mnToClientId.get(mn);
            if (existingClientId != null && !existingClientId.equals(currentClientId)) {
                log.warn("MN值 {} 已被客户端 {} 使用，断开旧连接", mn, existingClientId);
                disconnectClient(existingClientId);
            }
            
            // 更新映射关系
            SocketChannel channel = connectedClients.remove(currentClientId);
            String clientAddress = clientInfo.remove(currentClientId);
            String oldMn = clientIdToMn.remove(currentClientId);
            
            if (oldMn != null) {
                mnToClientId.remove(oldMn);
            }
            
            connectedClients.put(newClientId, channel);
            clientInfo.put(newClientId, clientAddress);
            mnToClientId.put(mn, newClientId);
            clientIdToMn.put(newClientId, mn);
            
            log.info("更新客户端ID: {} -> {} (MN: {})", currentClientId, newClientId, mn);
            
        } catch (Exception e) {
            log.error("更新客户端ID失败", e);
        }
    }
    
    /**
     * 发送错误响应
     */
    private void sendErrorResponse(SocketChannel clientChannel, String errorCode, String errorMsg) {
        try {
            byte[] errorResponse = packetParser.buildErrorResponse(errorCode, errorMsg);
            if (errorResponse != null) {
                sendResponse(clientChannel, errorResponse);
            }
        } catch (Exception e) {
            log.error("发送错误响应失败", e);
        }
    }
    
    /**
     * 向指定客户端发送消息
     * 
     * @param clientId 客户端ID
     * @param message 要发送的消息
     * @return 是否发送成功
     */
    public boolean sendMessageToClient(String clientId, String message) {
        if (!message.endsWith("\r\n")){
            message = message + "\r\n";
        }
        SocketChannel clientChannel = connectedClients.get(clientId);
        if (clientChannel == null) {
            log.warn("客户端不存在: {}", clientId);
            return false;
        }
        
        try {
            byte[] data = message.getBytes(StandardCharsets.UTF_8);
            sendResponse(clientChannel, data);
            log.info("已向客户端 {} 发送消息: {}", clientId, message);
            return true;
        } catch (Exception e) {
            log.error("向客户端 {} 发送消息失败", clientId, e);
            return false;
        }
    }
    
    /**
     * 向所有客户端广播消息
     * 
     * @param message 要广播的消息
     * @return 成功发送的客户端数量
     */
    public int broadcastMessage(String message) {
        int successCount = 0;
        for (var entry : connectedClients.entrySet()) {
            String clientId = entry.getKey();
            SocketChannel clientChannel = entry.getValue();
            
            try {
                byte[] data = message.getBytes(StandardCharsets.UTF_8);
                sendResponse(clientChannel, data);
                successCount++;
                log.debug("已向客户端 {} 广播消息", clientId);
            } catch (Exception e) {
                log.error("向客户端 {} 广播消息失败", clientId, e);
            }
        }
        
        log.info("广播消息完成，成功发送给 {} 个客户端", successCount);
        return successCount;
    }
    
    /**
     * 获取所有连接的客户端信息
     * 
     * @return 客户端信息映射，key为客户端ID，value为客户端地址
     */
    public ConcurrentHashMap<String, String> getConnectedClients() {
        return new ConcurrentHashMap<>(clientInfo);
    }
    
    /**
     * 获取当前连接数
     * 
     * @return 当前连接的客户端数量
     */
    public int getConnectedClientsCount() {
        return connectedClients.size();
    }
    
    /**
     * 检查指定客户端是否已连接
     * 
     * @param clientId 客户端ID
     * @return 是否已连接
     */
    public boolean isClientConnected(String clientId) {
        return connectedClients.containsKey(clientId);
    }
    
    /**
     * 断开指定客户端连接
     * 
     * @param clientId 客户端ID
     * @return 是否成功断开
     */
    public boolean disconnectClient(String clientId) {
        SocketChannel clientChannel = connectedClients.remove(clientId);
        if (clientChannel != null) {
            try {
                clientChannel.close();
                clientInfo.remove(clientId);
                
                // 清理MN相关映射
                String mn = clientIdToMn.remove(clientId);
                if (mn != null) {
                    mnToClientId.remove(mn);
                    log.info("已断开客户端连接: {} (MN: {})", clientId, mn);
                } else {
                    log.info("已断开客户端连接: {}", clientId);
                }
                return true;
            } catch (IOException e) {
                log.error("断开客户端连接失败: {}", clientId, e);
            }
        }
        return false;
    }
    
    /**
     * 停止服务器
     */
    public void stopServer() {
        running = false;
        
        try {
            // 关闭所有客户端连接
            for (var entry : connectedClients.entrySet()) {
                try {
                    entry.getValue().close();
                } catch (IOException e) {
                    log.error("关闭客户端连接失败: {}", entry.getKey(), e);
                }
            }
            connectedClients.clear();
            clientInfo.clear();
            mnToClientId.clear();
            clientIdToMn.clear();
            
            // 停止所有定时任务
            stopAllScheduledRequests();
            
            if (selector != null) {
                selector.close();
            }
            if (serverSocketChannel != null) {
                serverSocketChannel.close();
            }
            if (executorService != null) {
                executorService.shutdown();
            }
            if (scheduledExecutorService != null) {
                scheduledExecutorService.shutdown();
            }
            log.info("HJ212服务器已停止");
        } catch (IOException e) {
            log.error("停止HJ212服务器失败", e);
        }
    }
    
    /**
     * 根据MN值获取客户端ID
     * 
     * @param mn 机器标识码
     * @return 客户端ID，如果不存在则返回null
     */
    public String getClientIdByMn(String mn) {
        return mnToClientId.get(mn);
    }
    
    /**
     * 根据MN值获取客户端连接
     * 
     * @param mn 机器标识码
     * @return 客户端SocketChannel，如果不存在则返回null
     */
    public SocketChannel getClientByMn(String mn) {
        String clientId = mnToClientId.get(mn);
        if (clientId != null) {
            return connectedClients.get(clientId);
        }
        return null;
    }
    
    /**
     * 根据MN值发送消息
     * 
     * @param mn 机器标识码
     * @param message 要发送的消息
     * @return 是否发送成功
     */
    public boolean sendMessageByMn(String mn, String message) {
        String clientId = mnToClientId.get(mn);
        if (clientId == null) {
            log.warn("未找到MN值为 {} 的客户端", mn);
            return false;
        }
        return sendMessageToClient(clientId, message);
    }
    
    /**
     * 根据MN值断开客户端连接
     * 
     * @param mn 机器标识码
     * @return 是否成功断开
     */
    public boolean disconnectClientByMn(String mn) {
        String clientId = mnToClientId.get(mn);
        if (clientId == null) {
            log.warn("未找到MN值为 {} 的客户端", mn);
            return false;
        }
        return disconnectClient(clientId);
    }
    
    /**
     * 检查指定MN值的客户端是否已连接
     * 
     * @param mn 机器标识码
     * @return 是否已连接
     */
    public boolean isClientConnectedByMn(String mn) {
        return mnToClientId.containsKey(mn);
    }
    
    /**
     * 获取所有已连接的MN值列表
     * 
     * @return MN值列表
     */
    public java.util.Set<String> getConnectedMns() {
        return new java.util.HashSet<>(mnToClientId.keySet());
    }
    
    /**
     * 获取MN值到客户端信息的映射
     * 
     * @return MN值到客户端信息的映射
     */
    public java.util.Map<String, String> getMnToClientInfo() {
        java.util.Map<String, String> result = new java.util.HashMap<>();
        for (var entry : mnToClientId.entrySet()) {
            String mn = entry.getKey();
            String clientId = entry.getValue();
            String clientAddress = clientInfo.get(clientId);
            result.put(mn, clientAddress);
        }
        return result;
    }
    
    /**
     * 启动对指定MN值机器的定时请求
     * 
     * @param mn 机器标识码
     * @param requestContent 请求内容
     * @return 是否成功启动定时任务
     */
    public boolean startScheduledRequest(String mn, String requestContent) {
        try {
            // 检查客户端是否已连接
            if (!isClientConnectedByMn(mn)) {
                log.warn("客户端未连接，无法启动定时请求: {}", mn);
                return false;
            }
            
            // 检查是否已有定时任务
            if (scheduledTasks.containsKey(mn)) {
                log.warn("MN值 {} 已有定时任务在运行", mn);
                return false;
            }
            
            // 创建定时任务
            ScheduledFuture<?> task = scheduledExecutorService.scheduleAtFixedRate(() -> {
                try {
                    boolean success = sendMessageByMn(mn, requestContent);
                    if (success) {
                        log.debug("定时请求发送成功 - MN: {}, 内容: {}", mn, requestContent);
                    } else {
                        log.warn("定时请求发送失败 - MN: {}, 内容: {}", mn, requestContent);
                    }
                } catch (Exception e) {
                    log.error("定时请求执行异常 - MN: {}, 内容: {}", mn, requestContent, e);
                }
            }, 0, 1, TimeUnit.MINUTES); // 立即开始，每分钟执行一次
            
            scheduledTasks.put(mn, task);
            log.info("启动定时请求成功 - MN: {}, 内容: {}, 间隔: 1分钟", mn, requestContent);
            return true;
            
        } catch (Exception e) {
            log.error("启动定时请求失败 - MN: {}", mn, e);
            return false;
        }
    }
    
    /**
     * 停止对指定MN值机器的定时请求
     * 
     * @param mn 机器标识码
     * @return 是否成功停止定时任务
     */
    public boolean stopScheduledRequest(String mn) {
        try {
            ScheduledFuture<?> task = scheduledTasks.remove(mn);
            if (task != null) {
                task.cancel(false);
                log.info("停止定时请求成功 - MN: {}", mn);
                return true;
            } else {
                log.warn("未找到MN值 {} 的定时任务", mn);
                return false;
            }
        } catch (Exception e) {
            log.error("停止定时请求失败 - MN: {}", mn, e);
            return false;
        }
    }
    
    /**
     * 检查指定MN值是否有定时任务在运行
     * 
     * @param mn 机器标识码
     * @return 是否有定时任务在运行
     */
    public boolean hasScheduledRequest(String mn) {
        return scheduledTasks.containsKey(mn);
    }
    
    /**
     * 获取所有正在运行的定时任务MN值列表
     * 
     * @return 定时任务MN值列表
     */
    public java.util.Set<String> getScheduledMns() {
        return new java.util.HashSet<>(scheduledTasks.keySet());
    }
    
    /**
     * 停止所有定时任务
     */
    public void stopAllScheduledRequests() {
        try {
            for (var entry : scheduledTasks.entrySet()) {
                String mn = entry.getKey();
                ScheduledFuture<?> task = entry.getValue();
                task.cancel(false);
                log.info("停止定时请求 - MN: {}", mn);
            }
            scheduledTasks.clear();
            log.info("所有定时请求已停止");
        } catch (Exception e) {
            log.error("停止所有定时请求失败", e);
        }
    }
    
    /**
     * 检查服务器是否正在运行
     */
    public boolean isRunning() {
        return running;
    }
}
